#pragma once

//根据题目list文件，加载所有的题目信息到内存中：首先题目不可能会很多，而且题目的信息并不会很大
//直接与数据交互的模块，涉及题库等

#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <cassert>
#include <cstdlib>
#include <fstream>
#include <cstdlib>
#include "../../comm/log.hpp"
#include "../../comm/util.hpp"
#include <mysql/mysql.h>

namespace ns_model
{
    using namespace ns_util;
    using namespace ns_log;
   
    struct Question{
        std::string number;//题目编号，唯一
        std::string title; //题目标题
        std::string star;  //难度:简单，中等，困难
        int cpu_limit;     //题目的时间要求
        int mem_limit;     //题目的空间要求
        std::string desc;  //题目描述
        std::string header;//预设给用户在线编辑器中的代码
        std::string tail;  //题目的测试代码，需要和header拼接

        std::string mode;//标明题目类型:IO或Interface
        std::string IO_Input_File;//测试IO型题目的输入重定向文件路径
        std::string IO_Output_File;//IO型题目的输出重定向文件
    };
    
    const std::string webroot = "./wwwroot/";//web根目录
    const std::string oj_questions = "oj_questions";//数据库中表名称
    const std::string host = "127.0.0.1";//mysqld的IP地址
    const std::string user = "oj_client";//用户名
    const std::string passwd = "Wunuofan123@%";//密码
    const std::string db = "oj";//数据库名称
    const unsigned int port = 3306;//端口


    //与数据进行交互
    class Model
    {
        //相关信息直接在数据库中,直接从数据中读取:当然也可以预先加载
        //此处直接实时读取
    private:
        bool QueryMysql(const std::string &sql,std::vector<Question> &out)
        {
            //创建mysql句柄
            MYSQL *my = mysql_init(nullptr);
            if(nullptr == mysql_real_connect(my,host.c_str(),user.c_str(),passwd.c_str(),db.c_str(),port,nullptr,0))
            {
                LOG(LogLevel::FATAL) << "连接数据库失败!";
                return false;
            }
            LOG(LogLevel::INFO) << "连接数据库成功";

            // 一定要设置解析网络中接收到数据的编码格式:本质就是解析方式,在实际接收数据前设置即可
            mysql_set_character_set(my,"utf8mb4");
            
            //mysql_query成功时返回0
            if(mysql_query(my,sql.c_str()))
            {
                LOG(LogLevel::ERROR) << sql << " execute error";
                return false;
            }
            
            //从服务器中一次性加载全部结果
            MYSQL_RES *res = mysql_store_result(my);
            if(!res)
            {
                LOG(LogLevel::ERROR) << "mysql_store_result error";
                return false;
            }
            
            //分析结果:拿到提取结果的行列数
            int rows = mysql_num_rows(res);
            int cols = mysql_num_fields(res);
            
            Question q;
            for(int i = 0;i < rows;i++)
            {
                MYSQL_ROW row = mysql_fetch_row(res);//拿到一行数据
                if(!row)
                {
                    LOG(LogLevel::ERROR) << "mysql_fetch_row error";
                }

                //将所有从mysql中读取到数据存储到Question中
                q.number = row[0];
                q.title = row[1];
                q.star = row[2];
                q.desc = row[3];
                q.header = row[4];
                q.tail = row[5];
                q.cpu_limit = atoi(row[6]);
                q.mem_limit = atoi(row[7]);
                q.mode = row[8];
                if(q.mode == "IO")
                {
                    q.IO_Input_File = row[9];
                    q.IO_Output_File = row[10];
                }

                out.push_back(std::move(q));
            }

            //注意最后调用关闭相关接口，释放相关资源
            mysql_free_result(res);
            mysql_close(my);
            return true;
        }
    public:
        Model()
        {
            
        }
        //获取所有问题
        bool GetAllQuestions(std::vector<Question>& out)
        {
            std::string sql = "select * from ";
            sql += oj_questions;
            return QueryMysql(sql,out);
        }

        //获取一个问题
        bool GetOneQuestion(const std::string &number,Question& q)
        {
            std::string sql = "select * from ";
            sql += oj_questions;
            sql += " where id=";
            sql += number;
            std::vector<Question> res;//与GetAllQuestions使用同一个QueryMysql接口
            if(QueryMysql(sql,res))
            {
                if(res.size() == 1)
                {
                    q = res[0];
                    return true;
                }
            }
            return false;       
        }

        //获取OJ首页内容
        bool GetHomePage(std::string &out)
        {
            std::string root = webroot + "index.html";
            out = FileUtil::ReadFile(root);
            if(out.size())
                return true;
            return false;
        }
        ~Model()
        {}
    };
}

